package com.genreate.util.dbUtil;

import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Page;
import cn.hutool.db.*;
import cn.hutool.db.ds.simple.SimpleDataSource;
import cn.hutool.db.handler.RsHandler;
import cn.hutool.db.sql.SqlBuilder;
import com.genreate.constant.ServiceException;
import com.genreate.util.autoBuild.contsant.DatabaseType;
import com.genreate.constant.ErrorStatus;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;

/**
 * 注释
 *
 * @author lys
 * @date 2022/12/5
 */
public class JdbcDbUtil {
    static {
        GlobalDbConfig.setCaseInsensitive(false);
    }

    /**
     * 得到数据源
     *
     * @param jdbcConfig jdbc域
     * @return {@link Db}
     */
    public static Db getDatabase(JdbcConfig jdbcConfig){
        Db db = null;
        try {
            testConnection(jdbcConfig);
            db = getDb(jdbcConfig);
        }catch (Exception e){
            //连接失败，使用本地数据库
//            db = DbUtil.getDb();
            e.printStackTrace();
        }
        return db;
    }

    /**
     * 测试连接
     *
     * @param jdbcConfig jdbc域
     * @return {@link Connection}
     */
    public static Connection testConnection(JdbcConfig jdbcConfig){
        try {
            return Db.use(getDataSource(jdbcConfig)).getConnection();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
            throw new ServiceException(ErrorStatus.EXCEPTION.getCode(), "数据库连接失败，请检查配置");
        }
    }

    /**
     * 获取数据来源
     *
     * @param jdbcConfig jdbc域
     * @return {@link DataSource}
     */
    public static DataSource getDataSource(JdbcConfig jdbcConfig) {
        Opt.ofNullable(jdbcConfig).orElseThrow(()->new ServiceException(ErrorStatus.EXCEPTION.getCode(), "数据库配置为空"));
        if (StrUtil.isBlank(jdbcConfig.getType())) {
            throw new ServiceException(ErrorStatus.EXCEPTION.getCode(), "连接数据库类型未知");
        }
        if (StrUtil.isBlank(jdbcConfig.getIp()) || StrUtil.isBlank(jdbcConfig.getPort()) || StrUtil.isBlank(jdbcConfig.getDatabaseName())) {
            throw new ServiceException(ErrorStatus.EXCEPTION.getCode(), "连接数据库ip、端口或库名为空");
        }
        String userName = jdbcConfig.getUserName();
        String passWord = jdbcConfig.getPassword();
        if (StrUtil.isBlank(userName) || StrUtil.isBlank(passWord)) {
            throw new ServiceException(ErrorStatus.EXCEPTION.getCode(), "连接数据库用户名密码存在空值");
        }
        String dbUrl = null;
        String type = jdbcConfig.getType();
        if (StrUtil.equalsIgnoreCase(DatabaseType.MYSQL.getType(), type)) {
            dbUrl = StrUtil.format("jdbc:mysql://{}:{}/{}?useUnicode=true&characterEncoding=utf8" +
                    "&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true" +
                    "&allowMultiQueries=true",jdbcConfig.getIp(),jdbcConfig.getPort(),jdbcConfig.getDatabaseName());
        }
        //jdk17连接sqlserver会失败
        else if (StrUtil.equalsIgnoreCase(DatabaseType.SQLSERVER.getType(), jdbcConfig.getType())) {
            dbUrl = StrUtil.format("jdbc:sqlserver://{}:{};databasename={}",jdbcConfig.getIp(),jdbcConfig.getPort(),jdbcConfig.getDatabaseName());
        } else {
            throw new ServiceException(ErrorStatus.EXCEPTION.getCode(), "只支持mysql和SqlServer数据库类型导入导出");
        }
        return new SimpleDataSource(dbUrl, userName, passWord);
    }

    /**
     * 更新
     *
     * @param record 记录
     * @param where  在哪里
     * @return int
     */
    public static int update(JdbcConfig jdbcConfig, Entity record, Entity where) {
        try {
            return getDb(jdbcConfig).update(record, where);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 插入
     *
     * @param records 记录
     * @return int
     */
    public static int[] insert(JdbcConfig jdbcConfig, Collection<Entity> records) {
        try {
            return getDb(jdbcConfig).insert(records);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return new int[]{-1};
    }

    /**
     * 插入
     *
     * @param record 记录
     * @return int
     */
    public static int insert(JdbcConfig jdbcConfig, Entity record) {
        try {
            return getDb(jdbcConfig).insert(record);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return -1;
    }

    /**
     * 查询
     *
     * @param sql    查询语句
     * @param params 参数
     * @return 结果对象
     * @throws SQLException SQL执行异常
     * @since 3.1.1
     */
    public static List<Entity> querys(JdbcConfig jdbcConfig, String sql, Object... params) {
        try {
            return getDb(jdbcConfig).query(sql, params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 查询单条记录
     *
     * @param sql    查询语句
     * @param params 参数
     * @return 结果对象
     * @throws SQLException SQL执行异常
     */
    public static Entity query(JdbcConfig jdbcConfig, String sql, Object... params) {
        try {
            return getDb(jdbcConfig).queryOne(sql, params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 执行非查询语句<br>
     * 语句包括 插入、更新、删除
     *
     * @param sql    SQL
     * @param params 参数
     * @return 影响行数
     * @throws SQLException SQL执行异常
     */
    public static int execute(JdbcConfig jdbcConfig, String sql, Object... params) {
        try {
            return getDb(jdbcConfig).execute(sql, params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 分页查询<br>
     * 查询条件为多个key value对表示，默认key = value，如果使用其它条件可以使用：where.put("key", " &gt; 1")，value也可以传Condition对象，key被忽略
     *
     * @param <T>        结果对象类型
     * @param fields     返回的字段列表，null则返回所有字段
     * @param where      条件实体类（包含表名）
     * @param page       页码，0表示第一页
     * @param numPerPage 每页条目数
     * @param rsh        结果集处理对象
     * @return 结果对象
     * @throws SQLException SQL执行异常
     */
    public static <T> T page(JdbcConfig jdbcConfig, Collection<String> fields, Entity where, int page, int numPerPage, RsHandler<T> rsh) {
        try {
            return getDb(jdbcConfig).page(fields, where, page, numPerPage, rsh);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 分页查询<br>
     * 查询条件为多个key value对表示，默认key = value，如果使用其它条件可以使用：where.put("key", " &gt; 1")，value也可以传Condition对象，key被忽略
     *
     * @param <T>        结果对象类型
     * @param where      条件实体类（包含表名）
     * @param page       页码，0表示第一页
     * @param numPerPage 每页条目数
     * @param rsh        结果集处理对象
     * @return 结果对象
     * @throws SQLException SQL执行异常
     * @since 3.2.2
     */
    public static <T> T page(JdbcConfig jdbcConfig, Entity where, int page, int numPerPage, RsHandler<T> rsh) {
        try {
            return getDb(jdbcConfig).page(where, page, numPerPage, rsh);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 分页查询<br>
     * 查询条件为多个key value对表示，默认key = value，如果使用其它条件可以使用：where.put("key", " &gt; 1")，value也可以传Condition对象，key被忽略
     *
     * @param <T>   结果对象类型
     * @param where 条件实体类（包含表名）
     * @param page  分页对象
     * @param rsh   结果集处理对象
     * @return 结果对象
     * @throws SQLException SQL执行异常
     * @since 3.2.2
     */
    public static <T> T page(JdbcConfig jdbcConfig, Entity where, cn.hutool.db.Page page, RsHandler<T> rsh) {
        try {
            return getDb(jdbcConfig).page(where, page, rsh);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 分页查询<br>
     * 查询条件为多个key value对表示，默认key = value，如果使用其它条件可以使用：where.put("key", " &gt; 1")，value也可以传Condition对象，key被忽略
     *
     * @param <T>    结果对象类型
     * @param fields 返回的字段列表，null则返回所有字段
     * @param where  条件实体类（包含表名）
     * @param page   分页对象
     * @param rsh    结果集处理对象
     * @return 结果对象
     * @throws SQLException SQL执行异常
     */
    public static <T> T page(JdbcConfig jdbcConfig, Collection<String> fields, Entity where, cn.hutool.db.Page page, RsHandler<T> rsh) {
        try {
            return getDb(jdbcConfig).page(fields, where, page, rsh);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 分页查询<br>
     *
     * @param <T>    结果对象类型
     * @param sql    SQL构建器，可以使用{@link SqlBuilder#of(CharSequence)} 包装普通SQL
     * @param page   分页对象
     * @param rsh    结果集处理对象
     * @param params 参数
     * @return 结果对象
     * @throws SQLException SQL执行异常
     * @since 5.6.6
     */
    public static <T> T page(JdbcConfig jdbcConfig, CharSequence sql, cn.hutool.db.Page page, RsHandler<T> rsh, Object... params) {
        try {
            return getDb(jdbcConfig).page(sql, page, rsh, params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 分页查询
     *
     * @param <T>  处理结果类型，可以将ResultSet转换为给定类型
     * @param sql  SQL构建器
     * @param page 分页对象
     * @param rsh  结果集处理对象
     * @return 结果对象
     * @throws SQLException SQL执行异常
     */
    public static <T> T page(JdbcConfig jdbcConfig, SqlBuilder sql, cn.hutool.db.Page page, RsHandler<T> rsh) {
        try {
            return getDb(jdbcConfig).page(sql, page, rsh);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 分页查询
     *
     * @param sql    SQL语句字符串
     * @param page   分页对象
     * @param params 参数列表
     * @return 结果对象
     * @throws SQLException SQL执行异常
     * @since 5.5.3
     */
    public static PageResult<Entity> page(JdbcConfig jdbcConfig, CharSequence sql, cn.hutool.db.Page page, Object... params) {
        try {
            return getDb(jdbcConfig).page(sql, page, params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 分页查询
     *
     * @param sql      SQL语句字符串
     * @param pageNum  页数
     * @param pageSize 页码大小
     * @param params   参数列表
     * @return 结果对象
     * @throws SQLException SQL执行异常
     * @since 5.5.3
     */
    public static PageResult<Entity> page(JdbcConfig jdbcConfig, CharSequence sql, int pageNum, int pageSize, Object... params) {
        try {
            return getDb(jdbcConfig).page(sql, new cn.hutool.db.Page(pageNum, pageSize), params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 分页查询<br>
     * 查询条件为多个key value对表示，默认key = value，如果使用其它条件可以使用：where.put("key", " &gt; 1")，value也可以传Condition对象，key被忽略
     *
     * @param fields     返回的字段列表，null则返回所有字段
     * @param where      条件实体类（包含表名）
     * @param pageNumber 页码
     * @param pageSize   每页结果数
     * @return 结果对象
     * @throws SQLException SQL执行异常
     */
    public static PageResult<Entity> page(JdbcConfig jdbcConfig, Collection<String> fields, Entity where, int pageNumber, int pageSize) {
        try {
            return getDb(jdbcConfig).page(fields, where, pageNumber, pageSize);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 分页查询<br>
     * 查询条件为多个key value对表示，默认key = value，如果使用其它条件可以使用：where.put("key", " &gt; 1")，value也可以传Condition对象，key被忽略
     *
     * @param fields 返回的字段列表，null则返回所有字段
     * @param where  条件实体类（包含表名）
     * @param page   分页对象
     * @return 结果对象
     * @throws SQLException SQL执行异常
     */
    public static PageResult<Entity> page(JdbcConfig jdbcConfig, Collection<String> fields, Entity where, cn.hutool.db.Page page) {
        try {
            return getDb(jdbcConfig).page(fields, where, page);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 分页查询<br>
     * 查询条件为多个key value对表示，默认key = value，如果使用其它条件可以使用：where.put("key", " &gt; 1")，value也可以传Condition对象，key被忽略
     *
     * @param where      条件实体类（包含表名）
     * @param page       页码
     * @param numPerPage 每页条目数
     * @return 分页结果集
     * @throws SQLException SQL执行异常
     * @since 3.2.2
     */
    public static PageResult<Entity> page(JdbcConfig jdbcConfig, Entity where, int page, int numPerPage) {
        try {
            return getDb(jdbcConfig).page(where, page, numPerPage);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 分页查询<br>
     * 查询条件为多个key value对表示，默认key = value，如果使用其它条件可以使用：where.put("key", " &gt; 1")，value也可以传Condition对象，key被忽略
     *
     * @param where 条件实体类（包含表名）
     * @param page  分页对象
     * @return 分页结果集
     * @throws SQLException SQL执行异常
     */
    public static PageResult<Entity> page(JdbcConfig jdbcConfig, Entity where, Page page) {
        try {
            return getDb(jdbcConfig).page(where, page);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Db getDb(JdbcConfig jdbcConfig) {
        return Db.use(getDataSource(jdbcConfig));
    }
}
